//! @Author: DengLibin
//! @Date: Create in 2024-01-18 16:48:28
//! @Description: 签名 与验证签名

#[cfg(windows)]
extern crate winapi;

use winapi::um::errhandlingapi::GetLastError;
use winapi::um::winbase::FormatMessageW;
use winapi::um::winbase::FORMAT_MESSAGE_ALLOCATE_BUFFER;
use winapi::um::winbase::FORMAT_MESSAGE_FROM_SYSTEM;
use winapi::um::winbase::FORMAT_MESSAGE_IGNORE_INSERTS;
use winapi::um::winbase::FORMAT_MESSAGE_MAX_WIDTH_MASK;
use winapi::um::wincrypt::CryptAcquireContextA;

use winapi::um::wincrypt::CryptReleaseContext;

use winapi::um::wincrypt::HCRYPTPROV;

use winapi::shared::minwindef::BOOL;

use winapi::um::winnt::LANG_NEUTRAL;

use winapi::um::winnt::MAKELANGID;
use winapi::um::winnt::SUBLANG_DEFAULT;

use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::ptr;
use std::ptr::null_mut;

use rust_common::base64;
use winapi::shared::winerror::{
    ERROR_BUSY, ERROR_NOT_ENOUGH_MEMORY, NTE_BAD_FLAGS, NTE_BAD_KEYSET, NTE_KEYSET_ENTRY_BAD,
    NTE_KEYSET_NOT_DEF, NTE_NO_MEMORY, NTE_PROV_DLL_NOT_FOUND,
};

use std::ptr::null;

use winapi::um::wincrypt::{
    CryptCreateHash, CryptDestroyHash, CryptSignHashA, AT_SIGNATURE, HCRYPTHASH, PROV_RSA_FULL,
};
// CSP(加密服务提供商名称)
const PROVIDER_NAME: &str = "EnterSafe ePass3000GM CSP v1.0";

// usbkey中密钥容器名称
const CONTAINER: &str = "yiscn";

/// @Author: DengLibin
/// @Date: Create in 2024-01-18 11:12:43
/// @Description: rsa加解密
#[cfg(windows)]
pub fn hello_encrypt() {
    use winapi::{
        shared::minwindef::DWORD,
        um::{
            ncrypt::HCRYPTKEY,
            wincrypt::{
                CryptExportKey, CryptGetUserKey, CryptHashData, CryptImportKey,
                CryptVerifySignatureA, CALG_MD5, PUBLICKEYBLOB,
            },
        },
    };

    let mut sz_provider = PROVIDER_NAME.as_bytes().to_vec();
    //加一个0结尾
    sz_provider.push(0);

    //转i8
    let provider_str = sz_provider
        .iter()
        .map(|item| *item as i8)
        .collect::<Vec<i8>>();
    //转指针
    let provider = provider_str.as_ptr();
    //清理
    drop(sz_provider);

    let mut sz_container = CONTAINER.as_bytes().to_vec();
    //加一个0结尾
    sz_container.push(0);

    //转i8
    // let sz_container_str = sz_container
    //     .iter()
    //     .map(|item| *item as i8)
    //     .collect::<Vec<i8>>();
    //转指针
    //let sz_container_p = sz_container_str.as_ptr();
    //清理
    drop(sz_container);

    unsafe {
        // 定义加密上下文句柄
        let mut hprov: HCRYPTPROV = 0;
        let hp = &mut hprov;
        //获取上下文句柄
        let b: BOOL = CryptAcquireContextA(hp, null(), provider, PROV_RSA_FULL, 0);
        println!("初始化上下文结果：{},句柄:{}", b, *hp);

        let success: bool = b != 0;
        if !success {
            let error_code = GetLastError();
            if error_code as i32 == NTE_BAD_KEYSET {
                println!("无法打开密钥容器或容器不存在");
            } else if error_code == ERROR_NOT_ENOUGH_MEMORY {
                println!("操作系统在操作期间内存不足");
            } else if error_code == ERROR_BUSY {
                println!("另一个线程或进程正在使用此密钥容器");
            } else if error_code as i32 == NTE_KEYSET_ENTRY_BAD {
                println!("找到 pszContainer 密钥容器，但已损坏");
            } else if error_code as i32 == NTE_KEYSET_NOT_DEF {
                println!("请求的提供程序不存在。");
            } else if error_code as i32 == NTE_NO_MEMORY {
                println!("CSP 在操作期间内存不足。");
            } else if error_code as i32 == NTE_PROV_DLL_NOT_FOUND {
                println!("提供程序 DLL 文件不存在或不在当前路径上。");
            } else if error_code as i32 == NTE_BAD_FLAGS {
                println!("dwFlags 参数的值无效。");
            } else
            //容器已存在
            if error_code == 0x8009000F {
                println!("容器已存在:{}", CONTAINER);
            }
        }

        // 成功
        if success {
            //获取加密key
            let mut h_key: HCRYPTKEY = 0;

            if CryptGetUserKey(hprov, AT_SIGNATURE, &mut h_key) == 0 {
                println!("获取加密key错误");
                show_error();
            } else {
                println!("获取加密key成功:{}", h_key);
            }

            //第一次导出密钥，获取长度
            let mut dw_blob_len: DWORD = 0;
            //密钥缓冲区
            let mut key_buf = vec![];

            if CryptExportKey(h_key, 0, PUBLICKEYBLOB, 0, null_mut(), &mut dw_blob_len) == 0
            //public key blob length
            {
                println!("第一次导出密钥失败");
                show_error();
            } else {
                println!("第一次导出密钥成功,长度:{}", dw_blob_len);
                //根据长度创建缓冲区
                key_buf = vec![0; dw_blob_len as usize];
                if CryptExportKey(
                    h_key,
                    0,
                    PUBLICKEYBLOB,
                    0,
                    key_buf.as_mut_ptr(),
                    &mut dw_blob_len,
                ) == 0
                {
                    println!("第二次导出密钥失败");
                    show_error();
                } else {
                    println!("第二次导出密钥成功:{}", base64::encode_bytes(&key_buf[..]));
                }
            }

            //创建哈希对象
            let mut hash_key: HCRYPTHASH = 0;

            // digest signed data
            let data = "The data that is to be hashed and signed."
                .as_bytes()
                .to_vec(); //data to be signed
            let data_len = data.len() as u32;

            //接收需要的缓冲区大小
            let mut sign_buffer_len = 0;
            //分配签名缓冲区
            let mut sign_buffer = vec![];

            if CryptCreateHash(hprov, CALG_MD5, 0, 0, &mut hash_key) == 0 {
                println!("创建哈希对象错误");
                show_error();
            } else {
                println!("创建哈希对象成功:{}", hash_key);

                //对签名数据进行hash
                if CryptHashData(hash_key, data.as_ptr(), data_len, 0) == 0 {
                    println!("签名数据hash失败");
                    show_error();
                } else {
                    println!("签名数据hash成功");
                }

                //函数对数据进行签名,先传null, 用于设置缓冲区大小以进行内存分配
                //第一次签名获取缓冲区大小
                if CryptSignHashA(
                    hash_key,
                    AT_SIGNATURE,
                    null(), //传null
                    0,
                    null_mut(),
                    &mut sign_buffer_len,
                ) == 0
                {
                    println!("第一次签名失败");
                    show_error();
                } else {
                    println!("第一次签名成功,缓冲区大小:{}", sign_buffer_len);
                    //分配缓冲区
                    sign_buffer = vec![0; sign_buffer_len as usize];
                    if CryptSignHashA(
                        hash_key,
                        AT_SIGNATURE,
                        null(), //传null
                        0,
                        sign_buffer.as_mut_ptr(),
                        &mut sign_buffer_len,
                    ) == 0
                    {
                        println!("第二次签名失败");
                        show_error();
                    } else {
                        println!("第二次签名成功,数据大小:{}", sign_buffer_len);
                        let s = base64::encode_bytes(&sign_buffer[..]);
                        println!("签名成功:{}", s);
                    }
                }

                if CryptDestroyHash(hash_key) == 0 {
                    println!("销毁hash对象失败");
                    show_error();
                } else {
                    println!("销毁hash对象成功")
                }
            }

            // 验证签名
            let mut h_pub_key: HCRYPTKEY = 0;
            if CryptImportKey(hprov, key_buf.as_ptr(), dw_blob_len, 0, 0, &mut h_pub_key) == 0 {
                println!("导入密钥失败");
                show_error();
            } else {
                println!("导入密钥成功:{}", h_pub_key);

                if CryptCreateHash(hprov, CALG_MD5, 0, 0, &mut hash_key) == 0 {
                    println!("创建hash对象失败");
                    show_error();
                } else {
                    println!("创建hash对象成功");

                    //对签名数据进行hash
                    if CryptHashData(hash_key, data.as_ptr(), data_len, 0) == 0 {
                        println!("签名数据hash失败");
                        show_error();
                    } else {
                        println!("签名数据hash成功");
                        if CryptVerifySignatureA(
                            hash_key,
                            sign_buffer.as_ptr(), //signature
                            sign_buffer_len,
                            h_pub_key, //signer's public key
                            null(),
                            0,
                        ) == 0
                        {
                            println!("验证签名失败");
                            show_error();
                        } else {
                            println!("验证签名成功");
                        }
                    }
                }

                if CryptDestroyHash(hash_key) == 0 {
                    println!("销毁hash对象失败");
                    show_error();
                } else {
                    println!("销毁hash对象成功")
                }
            }

            let b: BOOL = CryptReleaseContext(hprov, 0);
            println!("释放结果:{}", b);
        } else {
            println!("获取上下文句柄错误");
            show_error();
        }
    }
}

#[allow(dead_code)]
fn show_error() {
    let error_code = 123; // 假设这是一个错误代码
    let mut buffer = [0; 256]; // 分配一个足够大的缓冲区
    let buffer_ptr: *mut u16 = buffer.as_mut_ptr();

    let flags = FORMAT_MESSAGE_ALLOCATE_BUFFER
        | FORMAT_MESSAGE_FROM_SYSTEM
        | FORMAT_MESSAGE_IGNORE_INSERTS
        | FORMAT_MESSAGE_MAX_WIDTH_MASK;
    let result = unsafe {
        FormatMessageW(
            flags,
            ptr::null(),
            error_code,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) as u32,
            buffer_ptr,
            256,
            ptr::null_mut(),
        )
    };

    if result == 0 {
        // 如果FormatMessageA失败，获取错误代码
        let last_error = unsafe { winapi::um::errhandlingapi::GetLastError() };
        eprintln!("FormatMessageA failed with error code: {}", last_error);
    } else {
        // 成功获取消息，打印出来
        let wide_message = unsafe {
            // 将宽字符指针转换为OsString
            OsString::from_wide(&buffer)
        };
        let message = wide_message.to_string_lossy().into_owned();
        println!("Message: {}", message);
    }
}
